package com.apobates.forum.toro.core;

import com.apobates.forum.toro.utils.CommonHelper;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 第八版
 * 基于头尾分隔符的下标配对匹配
 * @author xiaofanku@live.cn
 * @since 20220829
 */
public class SpaceMatcher {
    // 头分隔符
    private final Set<String> heads;
    // 尾分隔符
    private final Set<String> tails;
    private final static Logger logger = LoggerFactory.getLogger(SpaceMatcher.class);

    public SpaceMatcher(Set<String> heads, Set<String> tails) {
        this.heads = new HashSet<>(heads);
        this.tails = new HashSet<>(tails);
    }

    public Collection<SpaceMatcheResult> match(final String express){
        // 头的下标集合
        Map<String, List<Integer>> hins = findIndexSet(express, heads, "head-S");
        // 尾的下标集合
        Map<String,List<Integer>> tins = findIndexSet(express, tails, "tail-S");
        // 头和尾分隔符下标配对
        List<Pair<Integer,Integer>> indexSpace = buildIndexSpace(hins, tins);
        List<SpaceMatcheResult> data = new ArrayList<>() ;
        // 构建匹配的结果集
        for (Pair<Integer,Integer> p : indexSpace){
            int offset = p.getLeft();
            String headSymbol = seekMapContainsKey(hins, offset);
            String tailSymbol = seekMapContainsKey(tins, p.getRight());
            if(headSymbol.equals("-1") || tailSymbol.equals("-1")){
                continue;
            }
            String oss = express.substring(offset+headSymbol.length(), p.getRight());
            SpaceMatcheResult result = new SpaceMatcheResult(offset, p.getRight(), oss, headSymbol, tailSymbol);
            data.add(result);
            // 打印调试日志
            if(logger.isDebugEnabled()) {
                logger.debug(result.toString());
            }
        }
        return data;
    }
    // 查找某个下标对应的word值
    // 失败时返回-1
    private String seekMapContainsKey(Map<String,List<Integer>> target, int value){
        return target.entrySet().stream().filter(entry->entry.getValue().contains(value)).map(Map.Entry::getKey).findFirst().orElse("-1");
    }
    // 0-12-27-40-54-67-80-93
    // 0<M(>/          12,40,67,93<:>/ 27<)#R(>/        54<)&ML(>/   80<)#RJ(>
    // 12-27-40-54-67-80-93-106
    // 12,40,67,93<:>/ 27<)#R(>/       27,54,80,106<)>/ 54<)&ML(>/   80<)#RJ(>
    /*
    List<Pair<Integer,Integer>> indexPair = List.of(
                Pair.of(0, 12),
                Pair.of(12, 27),
                Pair.of(27, 40),
                Pair.of(40, 54),
                Pair.of(54, 67),
                Pair.of(67, 80),
                Pair.of(80, 93),
                Pair.of(93, 106)
    );
    */
    // 配对下标组合
    private List<Pair<Integer,Integer>> buildIndexSpace(Map<String, List<Integer>> headIndex, Map<String, List<Integer>> tailIndex){
        List<Integer> hs = headIndex.entrySet().stream().map(Map.Entry::getValue).flatMap(Collection::stream).distinct().sorted().collect(Collectors.toList());
        List<Integer> ts = tailIndex.entrySet().stream().map(Map.Entry::getValue).flatMap(Collection::stream).distinct().sorted().collect(Collectors.toList());
        List<Pair<Integer,Integer>> rs = new ArrayList<>();
        for (int i=0; i<hs.size(); i++){
            rs.add(Pair.of(hs.get(i), ts.get(i)));
        }
        // 打印调试日志
        if(logger.isDebugEnabled()) {
            rs.stream().forEach(p -> logger.debug(String.format("[SML][Pair] start: %d, finish: %d", p.getLeft(), p.getRight())));
        }
        return rs;
    }
    // key=分隔符. value=分隔符在表达式中的下标集合
    private Map<String,List<Integer>> findIndexSet(String express, Set<String> symbolSet, String sentinel){
        Map<String,List<Integer>> tmp = new HashMap<>();
        for(String t : symbolSet){
            List<Integer> temp = CommonHelper.getIndex(express, t);
            // 打印调试日志
            if(logger.isDebugEnabled()) {
                logger.debug(String.format("[SML]%s symbol: %s, index: %s", sentinel, t, CommonHelper.toString(temp, ",")));
            }
            tmp.put(t, temp);
        }
        return tmp;
    }
}
